"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from msnp.service import network
from msnp.service.network import logic
from msnp.service.network.model.Model import FingerValidationEntry, RingReportEntry, \
    BackupValidationEntry
from msnp.logger import Logger

"""
This module contains network validation functions, used during the development of the application.
"""

    
##########
## RING ##
##########

def generateRingReportEntry():
    """Generates a RingReportEntry-object that contains the own hashedNameLong, the own nameBundle and a dictionary of low level peers (key=hashedNameLong, value=nameBundle)."""
    
    myIdentity = network.NetworkController.getMySelf().getMyIdentity()
    backupContent = network.BackupController.selectBackupImResponsible()
    connectedIdentities = network.NetworkController.getMySelf().getAllConnectedIdentities()
    entry = RingReportEntry(myIdentity, backupContent, connectedIdentities)
    for llPeerName, llPeer in network.NetworkController.getMySelf().getLowLevelPeers().items():
        entry.addLowLevelPeer(llPeer.getIdentity())
            
    return entry
    
    
def printRingReportEntries(ring):
    """Prints the ring and at the end the number of super peers, low level peers and the sum of it."""
        
    numberOfLlPeers = 0
    Logger.info("Ring: ")
    for peer in ring:
        Logger.info(" - " + peer.getIdentity().getNameBundle())
        for llPeer in peer.getLowLevelPeers():
            numberOfLlPeers += 1
            Logger.info("   - " + llPeer.getNameBundle())
    Logger.info("Number of superpeers: " + str(len(ring)))
    Logger.info("Number of lowlevelpeers: " + str(numberOfLlPeers))
    Logger.info("Total number of peers: " + str(len(ring) + numberOfLlPeers))


def printRingValidation(ring):
    """Validates the ring and eventually prints wrong entries."""
        
    s = ""
    ok = True
    oldPeer = ring[-2]
    thisPeer = ring[-1]
    for nextPeer in ring:
        if logic.ResponsibilityLogic.isResponsible(oldPeer.getIdentity().getHashedNameLong(), thisPeer.getIdentity().getHashedNameLong(), nextPeer.getIdentity().getHashedNameLong()) == False:
            if ok:
                Logger.info("Ring NOT OK!!!")
                s += "Ring NOT OK!!!\n"
            Logger.info("wrong entry:")
            s += "wrong entry:\n"
            Logger.info(" - predecessor: " + oldPeer.getIdentity().getNameBundle())
            s += " - predecessor: " + oldPeer.getIdentity().getNameBundle() + "\n"
            Logger.info("   peer: " + thisPeer.getIdentity().getNameBundle())
            s += "   peer: " + thisPeer.getIdentity().getNameBundle() + "\n"
            Logger.info("   successor: " + nextPeer.getIdentity().getNameBundle())
            s += "   successor: " + nextPeer.getIdentity().getNameBundle() + "\n"
            ok = False
        oldPeer = thisPeer
        thisPeer = nextPeer
    if ok:
        Logger.info("Ring OK")
        s += "Ring OK"
    Logger.logResult(s) 
              

#####################
## LOW LEVEL PEERS ##
#####################
      
def printLowLevelValidation(ring):
    """Validates the low level peers of the ring and eventually prints wrong entries."""
        
    s = ""
    ok = True
    peers = []
    lastPeer = 0
    shouldBeSuperPeer = []
    thisPeerOk = True
    oldPeer = ring[-1]
    for thisPeer in ring:
        peerNameLong = long(thisPeer.getIdentity().getName(), 10)
        peers.append(peerNameLong)
        if lastPeer < peerNameLong:
            lastPeer = peerNameLong
        for llPeer in thisPeer.getLowLevelPeers():
            peerNameLong = long(llPeer.getName(), 10)
            peers.append(peerNameLong)
            if lastPeer < peerNameLong:
                lastPeer = peerNameLong
            if llPeer.getHashedNameLong() % 2 == 0:
                shouldBeSuperPeer.append(str(peerNameLong))
            if logic.ResponsibilityLogic.isResponsible(oldPeer.getIdentity().getHashedNameLong(), llPeer.getHashedNameLong(), thisPeer.getIdentity().getHashedNameLong()) == False:
                if ok:
                    Logger.info("Low Level Peers NOT OK!!!!!!")
                    s += "Low Level Peers NOT OK!!!!!!\n"
                if thisPeerOk:
                    Logger.info(thisPeer.getIdentity().getNameBundle() + " has wrong low level peers:")
                    s += thisPeer.getIdentity().getNameBundle() + " has wrong low level peers:\n"
                    thisPeerOk = False
                Logger.info(" - " + llPeer.getNameBundle())
                s += " - " + llPeer.getNameBundle() + "\n"
                ok = False
        oldPeer = thisPeer
        thisPeerOk = True
    if ok:
        Logger.info("Low Level Peers OK")
        s += "Low Level Peers OK\n"
    Logger.info("Low Level Peers that should be Super Peers:")
    s += "Low Level Peers that should be Super Peers:\n"
    for peer in shouldBeSuperPeer:
        Logger.info(" - " + peer)
        s += " - " + peer + "\n"
    Logger.logResult(s)
    
    
def __getMissingPeers(ring):
    """Collects missing peers."""
    
    peersInRing = []
    lastPeer = 0
    for peer in ring:
        peerNameLong = long(peer.getIdentity().getName(), 10)
        if lastPeer < peerNameLong:
            lastPeer = peerNameLong
        peersInRing.append(peerNameLong)
        for llPeer in peer.getLowLevelPeers():
            peerNameLong = long(llPeer.getName(), 10)
            if lastPeer < peerNameLong:
                lastPeer = peerNameLong
            peersInRing.append(peerNameLong)
    peersNotInRing = []
    for i in range(0, lastPeer):
        if peersInRing.__contains__(i) == False:
            peersNotInRing.append(i)
    return peersNotInRing


#################
## FINGERTABLE ##
#################
    
def checkFingertable(ring, fingerValidationDict):
    """Checks whether the own fingertable is correct or not.
        
    For the validation the current ring of peers is given.
    A list of non correct entries gets appended to the 'fingerValidationDict' with the own hashedNameLong as key and a 'FingerValidationEntry' as value.
    Such an FingerValidationEntry contains the index of the incorrect finger, the nameBundle of the current i-th finger, the string-value that got calculated for that finger and the nameBundle of the peer the finger should point to."""
        
    errorList = []
    myHashedNameLong = network.NetworkController.getMySelf().getMyIdentity().getHashedNameLong()
    for i, finger in network.NetworkController.getMySelf().getFingers().items():
        calculatedFingerLong = logic.FingerLogic.calculateFinger(myHashedNameLong, i, False)
        shouldBeFinger = None
        #search for the correct peer in the ring for the i-th finger
        oldPeer = ring[-1]
        for currentPeer in ring:
            oldPeerName = oldPeer.getIdentity().getHashedNameLong()
            currentPeerName = currentPeer.getIdentity().getHashedNameLong()
            if oldPeerName < currentPeerName:
                if currentPeerName >= calculatedFingerLong and oldPeerName < calculatedFingerLong:
                    #correct peer for the i-th finger found
                    shouldBeFinger = currentPeer
                    break
            else:
                if calculatedFingerLong > oldPeerName or calculatedFingerLong <= currentPeerName:
                    #correct peer for the i-th finger found
                    shouldBeFinger = currentPeer
                    break
            oldPeer = currentPeer
            
        if shouldBeFinger == None:
            #if the correct peer for the i-th finger is None there is an error
            errorList.append(FingerValidationEntry(i, finger.getNameBundle(), str(calculatedFingerLong), 'None'))
        else: 
            shouldBeFingerName = shouldBeFinger.getIdentity().getHashedNameLong()
            if finger == None:
                if shouldBeFingerName != myHashedNameLong:
                    #if the finger is None but the correct finger is not the peer itself there is an error
                    errorList.append(FingerValidationEntry(i, 'None', str(calculatedFingerLong), shouldBeFinger.getIdentity().getNameBundle()))
            else:
                if finger.getHashedNameLong() != shouldBeFingerName:
                    #if the correct finger is not the same peer as the finger there is an error
                    errorList.append(FingerValidationEntry(i, finger.getNameBundle(), str(calculatedFingerLong), shouldBeFinger.getIdentity().getNameBundle()))
    myNameBundle = network.NetworkController.getMySelf().getMyIdentity().getNameBundle()
    fingerValidationDict[myNameBundle] = errorList
    return fingerValidationDict
    

def printFingerValidationEntry(fingerValidationDict):
    """Prints all entries of the 'fingerValidationDict'.
        
    It contains the incorrect entries of the fingertables of all peers in the ring."""
        
    s = "\n"
    ok = True
    for nameBundle, errorList in fingerValidationDict.items():
        if len(errorList) > 0:
            ok = False
            Logger.info("Fingertables NOT OK!!!")
            s += "Fingertable NOT OK!!!\n"
            Logger.info(nameBundle + ':')
            s += nameBundle + ":\n"
            for errorEntry in errorList:
                if errorEntry != None:
                    index = 'None'
                    if errorEntry.getIndex() != None:
                        index = str(errorEntry.getIndex())
                    Logger.info(' - finger ' + index + ' not ok:')
                    s += " - finger " + index + " not ok:\n"
                    Logger.info('   currentFinger = ' + errorEntry.getCurrentFingerNameBundle())
                    s += '   currentFinger = ' + errorEntry.getCurrentFingerNameBundle() + "\n"
                    Logger.info('   calculatedFinger = ' + errorEntry.getCalculatedFingerString())
                    s += '   calculatedFinger = ' + errorEntry.getCalculatedFingerString() + "\n"
                    Logger.info('   shouldBeFinger = ' + errorEntry.getShouldBeFingerNameBundle())
                    s += '   shouldBeFinger = ' + errorEntry.getShouldBeFingerNameBundle() + "\n"
    if ok:
        Logger.info('All Fingertables OK')
        s += 'All Fingertables OK'
    Logger.logResult(s)
    

############
## BACKUP ##
############
    
def checkBackup(ring, backupValidationDict):
    """Validates the backups"""
    
    myName = network.NetworkController.getMySelf().getMyIdentity().getName()
    errorList = []
    resultString = __checkOwnBackup(ring)
    if len(resultString) > 1:
        errorList.append(BackupValidationEntry(myName, resultString))
    myName = network.NetworkController.getMySelf().getMyIdentity().getName()
    for ringReportEntry in ring:
        if ringReportEntry.contains(myName):
            myBackupForUsername = network.BackupController.selectBackupForBackupFrom(ringReportEntry.getIdentity().getName())
            resultString = __compareBackups(myBackupForUsername, ringReportEntry.getBackupContent())
            if len(resultString) > 1:
                errorList.append(BackupValidationEntry(ringReportEntry.getIdentity().getName(), resultString))
    backupValidationDict[myName] = errorList
    return backupValidationDict


def __checkOwnBackup(ring):
    """Validates the backup of the current peer"""
    
    backupImResponsible = network.BackupController.selectBackupImResponsible()
    myLowLevelPeers = network.NetworkController.getMySelf().getLowLevelPeers()
    myName = network.NetworkController.getMySelf().getMyIdentity().getName()
    numberOfPeers = 1
    resultString = "|"
    #check whether I am in my database
    peerFound = backupImResponsible.containsUsername(myName)
    if peerFound == False:
        resultString += myName + "|"
    #check if my low level peers are in my database
    for llpeerName, llpeer in myLowLevelPeers.items():
        numberOfPeers += 1
        peerFound = backupImResponsible.containsUsername(llpeer.getIdentity().getName())
        if peerFound == False:
            resultString += llpeer.getIdentity().getName() + "|"
    if numberOfPeers != len(backupImResponsible.getUsers()):
        resultString += "length -> " + str(numberOfPeers) + "vs" + str(len(backupImResponsible.getUsers()))
    return resultString
    
    
def __compareBackups(myBackup, otherBackup):
    """backup comparison"""
    
    returnString = "|"
    otherUsers = otherBackup.getUsers()
    if __compareUsers(myBackup, otherUsers) == False:
        returnString += "user/message|"
    otherUserGroups = otherBackup.getUserGroups()
    if __compareUserGroups(myBackup, otherUserGroups) == False:
        returnString += "userGroups|"
    otherGroups = otherBackup.getGroups()
    if __compareGroups(myBackup, otherGroups) == False:
        returnString += "groups/members|"
    return returnString
        
        
def __compareUsers(myBackup, otherUsers):
    """user comparison"""
    
    if len(otherUsers) == len(myBackup.getUsers()):
        for user in otherUsers:
            result = myBackup.containsUser(user)
            if result == False:
                return result
        return True
    return False 


def __compareUserGroups(myBackup, otherUserGroups):
    """user group relation comparison"""
    
    if len(otherUserGroups) == len(myBackup.getUserGroups()):
        for userGroup in otherUserGroups:
            result = myBackup.containsUserGroup(userGroup)
            if result == False:
                return result
        return True
    return False 


def __compareGroups(myBackup, otherGroups):
    """group comparison"""
    
    if len(otherGroups) == len(myBackup.getGroups()):
        for group in otherGroups:
            result = myBackup.containsGroup(group)
            if result == False:
                return result
        return True
    return False


def printBackupValidation(backupValidationDict):
    """prints the backup validation result"""
    
    s = ""
    ok = True
    for name, errorList in backupValidationDict.items():
        if len(errorList) > 0:
            ok = False
            Logger.info("Backup NOT OK!!!!")
            s += "Backup NOT OK!!!!\n"
            Logger.info(name + ":")
            s += name + ":\n"
            for entry in errorList:
                Logger.info(" - backupfrom: " + entry.getBackupFromName() + ", has error: " + entry.getErrorString())
                s += " - backupfrom: " + entry.getBackupFromName() + ", has error " + entry.getErrorString() + "\n"
    if ok:
        Logger.info("All Backups OK")
        s += "All Backups OK\n"
    Logger.logResult(s)        
    

def printAllPeersBackuped(ring):
    """prints all backuped peers in the ring"""
    
    s = ""
    lastPeer = 0
    backupedUsers = []
    for peer in ring:
        for user in peer.getBackupContent().getUsers():
            username = user.getUsername()
            if backupedUsers.__contains__(username) == False:
                backupedUsers.append(username)
                if lastPeer < long(username, 10):
                    lastPeer = long(username, 10)
            else:
                Logger.info("Peer " + username + " is backuped twices!")
                s += "Peer " + username + " is backuped twices!\n"
    if lastPeer + 1 == len(backupedUsers):
        Logger.info("All Peers backuped OK")
        s += "All Peers backuped OK"
    else:
        Logger.info("All Peers backuped NOT OK!!!!!")
        s += "All Peers backuped NOT OK!!!!!\n"
        for i in range(0, lastPeer):
            if backupedUsers.__contains__(str(i)) == False:
                Logger.info(" - Peer " + str(i) + " is nowhere backuped")
                s += " - Peer " + str(i) + " is nowhere backuped\n"
    Logger.info(" - " + str(len(backupedUsers)) + " users backuped - last Peer: " + str(lastPeer))
    s += " - " + str(len(backupedUsers)) + " users backuped - last Peer: " + str(lastPeer)
    Logger.logResult(s) 


